Passed
Push — master ( f6265f...99efad )
by Rafael S.
02:22
created

T_EXPORT ➔ ???   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
dl 0
loc 1
rs 10
c 0
b 0
f 0
nop 2
1
/*
2
 * byte-data: Pack and unpack binary data.
3
 * https://github.com/rochars/byte-data
4
 *
5
 * Copyright (c) 2017-2018 Rafael da Silva Rocha.
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining
8
 * a copy of this software and associated documentation files (the
9
 * "Software"), to deal in the Software without restriction, including
10
 * without limitation the rights to use, copy, modify, merge, publish,
11
 * distribute, sublicense, and/or sell copies of the Software, and to
12
 * permit persons to whom the Software is furnished to do so, subject to
13
 * the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be
16
 * included in all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
 *
26
 */
27
28
/**
29
 * @fileoverview Pack and unpack two's complement ints and unsigned ints.
30
 */
31
32
/**
33
 * A class to pack and unpack two's complement ints and unsigned ints.
34
 * 
35
 */
36
export default class Integer {
37
38
    /**
39
     * @param {number} bits Number of bits used by the data.
40
     * @param {boolean} signed True for signed types.
41
     * @throws {Error} if the number of bits is smaller than 1 or greater than 64.
42
     */
43
    constructor(bits, signed) {
44
        /**
45
         * The max number of bits used by the data.
46
         * @type {number}
47
         */
48
        this.bits = bits;
49
        /**
50
         * If this type it is signed or not.
51
         * @type {boolean}
52
         */
53
        this.signed = signed;
54
        /**
55
         * The number of bytes used by the data.
56
         * @type {number}
57
         */
58
        this.offset = 0;
59
        /**
60
         * Min value for numbers of this type.
61
         * @type {number}
62
         */
63
        this.min = -Infinity;
64
        /**
65
         * Max value for numbers of this type.
66
         * @type {number}
67
         */
68
        this.max = Infinity;
69
        /**
70
         * The practical number of bits used by the data.
71
         * @type {number}
72
         * @private
73
         */
74
        this.realBits_ = this.bits;
75
        /**
76
         * The mask to be used in the last byte.
77
         * @type {number}
78
         * @private
79
         */
80
        this.lastByteMask_ = 255;
81
        this.build_();
82
    }
83
84
    /**
85
     * Read one integer number from a byte buffer.
86
     * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
87
     * @param {number=} i The index to read.
88
     * @return {number}
89
     */
90
    read(bytes, i=0) {
91
        let num = 0;
92
        let x = this.offset - 1;
93
        while (x > 0) {
94
            num = (bytes[x + i] << x * 8) | num;
95
            x--;
96
        }
97
        num = (bytes[i] | num) >>> 0;
98
        return this.overflow_(this.sign_(num));
99
    }
100
101
    /**
102
     * Write one integer number to a byte buffer.
103
     * @param {!Array<number>} bytes An array of bytes.
104
     * @param {number} number The number.
105
     * @param {number=} j The index being written in the byte buffer.
106
     * @return {number} The next index to write on the byte buffer.
107
     */
108
    write(bytes, number, j=0) {
109
        number = this.overflow_(number);
110
        bytes[j++] = number & 255;
111
        for (let i = 2; i <= this.offset; i++) {
112
            bytes[j++] = Math.floor(number / Math.pow(2, ((i - 1) * 8))) & 255;
113
        }
114
        return j;
115
    }
116
117
    /**
118
     * Write one integer number to a byte buffer.
119
     * @param {!Array<number>} bytes An array of bytes.
120
     * @param {number} number The number.
121
     * @param {number=} j The index being written in the byte buffer.
122
     * @return {number} The next index to write on the byte buffer.
123
     * @private
124
     */
125
    writeEsoteric_(bytes, number, j=0) {
126
        number = this.overflow_(number);
127
        j = this.writeFirstByte_(bytes, number, j);
128
        for (let i = 2; i < this.offset; i++) {
129
            bytes[j++] = Math.floor(number / Math.pow(2, ((i - 1) * 8))) & 255;
130
        }
131
        if (this.bits > 8) {
132
            bytes[j++] = Math.floor(
133
                    number / Math.pow(2, ((this.offset - 1) * 8))) &
134
                this.lastByteMask_;
135
        }
136
        return j;
137
    }
138
139
    /**
140
     * Read a integer number from a byte buffer by turning int bytes
141
     * to a string of bits. Used for data with more than 32 bits.
142
     * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
143
     * @param {number=} i The index to read.
144
     * @return {number}
145
     * @private
146
     */
147
    readBits_(bytes, i=0) {
148
        let binary = '';
149
        let j = 0;
150
        while(j < this.offset) {
151
            let bits = bytes[i + j].toString(2);
152
            binary = new Array(9 - bits.length).join('0') + bits + binary;
153
            j++;
154
        }
155
        return this.overflow_(this.sign_(parseInt(binary, 2)));
156
    }
157
158
    /**
159
     * Build the type.
160
     * @throws {Error} if the number of bits is smaller than 1 or greater than 64.
161
     * @private
162
     */
163
    build_() {
164
        this.setRealBits_();
165
        this.setLastByteMask_();
166
        this.setMinMax_();
167
        this.offset = this.bits < 8 ? 1 : Math.ceil(this.realBits_ / 8);
168
        if ((this.realBits_ != this.bits) || this.bits < 8 || this.bits > 32) {
169
            this.write = this.writeEsoteric_;
170
            this.read = this.readBits_;
171
        }
172
    }
173
174
    /**
175
     * Sign a number.
176
     * @param {number} num The number.
177
     * @return {number}
178
     * @private
179
     */
180
    sign_(num) {
181
        if (num > this.max) {
182
            num -= (this.max * 2) + 2;
183
        }
184
        return num;
185
    }
186
187
    /**
188
     * Limit the value according to the bit depth in case of
189
     * overflow or underflow.
190
     * @param {number} value The data.
191
     * @return {number}
192
     * @private
193
     */
194
    overflow_(value) {
195
        if (value > this.max) {
196
            throw new Error('Overflow.');
197
        } else if (value < this.min) {
198
            throw new Error('Underflow.');
199
        }
200
        return value;
201
    }
202
203
    /**
204
     * Set the minimum and maximum values for the type.
205
     * @private
206
     */
207
    setMinMax_() {
208
        let max = Math.pow(2, this.bits);
209
        if (this.signed) {
210
            this.max = max / 2 -1;
211
            this.min = -max / 2;
212
        } else {
213
            this.max = max - 1;
214
            this.min = 0;
215
        }
216
    }
217
218
    /**
219
     * Set the practical bit number for data with bit count different
220
     * from the standard types (8, 16, 32, 40, 48, 64) and more than 8 bits.
221
     * @private
222
     */
223
    setRealBits_() {
224
        if (this.bits > 8) {
225
            this.realBits_ = ((this.bits - 1) | 7) + 1;
226
        }
227
    }
228
229
    /**
230
     * Set the mask that should be used when writing the last byte.
231
     * @private
232
     */
233
    setLastByteMask_() {
234
        let r = 8 - (this.realBits_ - this.bits);
235
        this.lastByteMask_ = Math.pow(2, r > 0 ? r : 8) -1;
236
    }
237
238
    /**
239
     * Write the first byte of a integer number.
240
     * @param {!Array<number>} bytes An array of bytes.
241
     * @param {number} number The number.
242
     * @param {number} j The index being written in the byte buffer.
243
     * @return {number} The next index to write on the byte buffer.
244
     * @private
245
     */
246
    writeFirstByte_(bytes, number, j) {
247
        if (this.bits < 8) {
248
            bytes[j++] = number < 0 ? number + Math.pow(2, this.bits) : number;
249
        } else {
250
            bytes[j++] = number & 255;
251
        }
252
        return j;
253
    }
254
}
255